﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Net;
using System.IO;
using Lidgren;
using Lidgren.Network;

namespace OnlineQuiz_Server
{
    public enum Headers : byte
    {
        NewPlayer,
        PlayerData,
        Text,
        PlayerDisconnected
    }

    public enum DataType : byte
    {
        String,
        Int,
        Bool,
        Float
    }

    public class Program
    {
        static NetPeerConfiguration Config;
        static NetServer Server;
        static List<Player> Players;

        static void Main(string[] args)
        {
            try
            {
                Config = new NetPeerConfiguration("Quiz");
                Players = new List<Player>();
                Config.MaximumConnections = 10;
                Config.Port = 7777;
                Server = new NetServer(Config);
                new Thread(new ThreadStart(ReceiveData)).Start();
                Server.Start();
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: " + ex.ToString());
            }
        }

        static void ReceiveData()
        {
            while (true)
            {
                NetIncomingMessage incmsg;
                NetOutgoingMessage outmsg;

                if ((incmsg = Server.ReadMessage()) != null)
                {
                    switch (incmsg.MessageType)
                    {
                        case NetIncomingMessageType.StatusChanged:

                            NetConnectionStatus status = (NetConnectionStatus)incmsg.ReadByte();
							string reason = incmsg.ReadString();
                            Console.WriteLine(incmsg.SenderConnection.RemoteUniqueIdentifier + " " + status + ": " + reason);
                            
                            if (status == NetConnectionStatus.Disconnected)
                            {
                                int playeridx = GetPlayerFromCon(incmsg.SenderConnection);
                                Players.RemoveAt(playeridx);
                                outmsg = Server.CreateMessage();
                                outmsg.Write((byte)Headers.PlayerDisconnected);
                                outmsg.Write(playeridx);
                                Server.SendToAll(outmsg, incmsg.SenderConnection, NetDeliveryMethod.ReliableSequenced, 0);
                            }

                            break;

                        case NetIncomingMessageType.Data:

                            Headers header = (Headers)incmsg.ReadByte();

                            switch (header)
                            {
                                case Headers.Text:

                                    string message = incmsg.ReadString();
                                    Console.WriteLine("Incoming Message: " + message);
                                    break;

                                case Headers.PlayerData:

                                    GetNewPlayerData(incmsg);
                                    break;

                                case Headers.NewPlayer:

                                    SendNewPlayerDataToNewPlayer(incmsg);
                                    break;
                            }

                            break;
                    }
                }
            }
        }

        static void GetNewPlayerData(NetIncomingMessage incmsg)
        {
            int newPlayerIdx = GetPlayerFromCon(incmsg.SenderConnection);
            incmsg.ReadAllProperties(Players[newPlayerIdx]);
            SendNewPlayerDataToAll(Players[newPlayerIdx], incmsg);
        }

        static void SendNewPlayerDataToNewPlayer(NetIncomingMessage incmsg)
        {
            Player newPlayer = new Player();
            newPlayer.ID = GeneratePlayerID();
            newPlayer.Name = "Player " + newPlayer.ID;
            newPlayer.Question = "None";
            newPlayer.Answer = "None";
            newPlayer.Connection = incmsg.SenderConnection;
            Players.Add(newPlayer);
            NetOutgoingMessage outmsg = Server.CreateMessage();
            outmsg.Write((byte)Headers.NewPlayer);
            outmsg.Write(Players.Count);

            if (Players.Count <= 1)
            {
                outmsg.WriteAllProperties(newPlayer);
            }
            else
            {
                foreach(Player player in Players) outmsg.WriteAllProperties(player);
            }

            Server.SendMessage(outmsg, incmsg.SenderConnection, NetDeliveryMethod.ReliableSequenced);
            SendNewPlayerDataToAll(newPlayer, incmsg);
        }

        static void SendNewPlayerDataToAll(Player player, NetIncomingMessage incmsg)
        {
            NetOutgoingMessage outmsg = Server.CreateMessage();
            outmsg.Write((byte)Headers.PlayerData);
            outmsg.Write(player.ID);
            outmsg.WriteAllProperties(player);
            Server.SendToAll(outmsg, incmsg.SenderConnection, NetDeliveryMethod.ReliableSequenced, 0);
        }

        static int GetPlayerFromCon(NetConnection con)
        {
            for (int i = 0; i < Players.Count; i++)
                if (Players[i].Connection == con) return i;

            return -1;
        }

        static int GeneratePlayerID()
        {
            int id = 1000;
            Random rnd = new Random();

            do
            {
                id = rnd.Next(1000, 5000);
            }
            while(MatchedID(id));
            return id;
        }

        static bool MatchedID(int id)
        {
            foreach (Player player in Players)
                if (player.ID == id) return true;

            return false;
        }
    }

    public class Player
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Question { get; set; }
        public string Answer { get; set; }
        public bool Turn;
        public NetConnection Connection;

        public Player()
        {
            Turn = false;
        }

        public override string ToString()
        {
            return string.Format("Name: {0}, Question: {1}, Answer {2}, Turn {3}", Name, Question, Answer, Turn);
        }
    }
}
